package com.fubowen.reader.vm

import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import com.fubowen.reader.book.impl.WebBookGetter
import com.fubowen.reader.db.bean.BookBean
import com.fubowen.reader.db.bean.BookChapterBean
import com.fubowen.reader.db.bean.WebSiteConfigBean
import com.fubowen.reader.db.databaseOfTheme
import com.fubowen.reader.db.model.Tree
import com.fubowen.reader.db.service.BookService
import com.fubowen.reader.ui.fragment.LINK_NAME
import com.fubowen.reader.ui.fragment.LINK_PATH
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Semaphore
import kotlin.coroutines.cancellation.CancellationException

class CacheViewModel(application: Application) : AndroidViewModel(application) {

    private val database by databaseOfTheme()
    private val bookService = BookService(database.bookDao())

    //region 选中的项目列表以及增删改查操作

    private val selectedList = mutableListOf<Tree>()
    private val selected = MutableSharedFlow<List<Tree>>(1)
    private val update = MutableSharedFlow<Tree>(1)
    private val size = MutableStateFlow(0)
    val selectedFlow = selected.asSharedFlow()
    val updateFlow = update.asSharedFlow()
    val sizeFlow = size.asSharedFlow()

    fun select(item: Tree) = viewModelScope.launch {
        selectedList.add(item)
        selected.emit(selectedList)
        size.value = selectedList.size
    }

    fun delete(item: Tree) = viewModelScope.launch {
        item.status = Tree.STATUS_NOT_START
        selectedList.remove(item)
        selected.emit(selectedList)
        size.value = selectedList.size
    }

    fun length() = selectedList.size

    fun update(item: Tree) = viewModelScope.launch {
        update.emit(item)
    }

    //endregion

    //region 下载选中的项目

    private var coroutineCount = MutableStateFlow(5)
    private var semaphore = Semaphore(coroutineCount.value)
    private val taskCache = mutableMapOf<String, Job>()
    var coroutineCountFlow = coroutineCount.asStateFlow()

    fun setCoroutineCount(count: Int) {
        coroutineCount.value = count
        semaphore = Semaphore(count)
    }

    private fun startInternal(item: Tree, onCompleted: () -> Unit): Boolean {
        if (semaphore.tryAcquire()) {
            item.status = Tree.STATUS_RUNNING
            update(item)
            val job = viewModelScope.launch(Dispatchers.IO) {
                try {
                    downloadInternal(item)
                } catch (_: CancellationException) {
                } catch (e: Exception) {
                    item.status = Tree.STATUS_ERROR
                    item.errMsg = e.message
                } finally {
                    update(item)
                }
            }
            job.invokeOnCompletion {
                semaphore.release()
                onCompleted()
            }
            taskCache[item.absolutePath] = job
            return true
        }
        return false
    }

    fun start(item: Tree) = viewModelScope.launch {
        if (semaphore.availablePermits > 0) {
            startInternal(item) { download() }
        } else {
            item.status = Tree.STATUS_WAITING
            update(item)
        }
    }

    fun stop(item: Tree) = taskCache.remove(item.absolutePath)?.cancel()

    fun download() {
        for (item in selectedList) {
            if (item.status == Tree.STATUS_NOT_START && !startInternal(item) { download() }) {
                break
            }
        }
    }

    fun downloadSite(url: String, isRemote: Boolean) {

    }

    //endregion

    private fun downloadInternal(item: Tree) {
        when (item.type) {
            Tree.TYPE_FOLDER -> {}
            Tree.TYPE_FILE -> {}
            Tree.TYPE_URL -> {
                val chapterList = WebBookGetter.getChapterList(item.absolutePath)
                val book = bookService.setOrGet(
                    BookBean(
                        name = item.name,
                        url = item.absolutePath,
                        chapterCount = chapterList.size,
                        type = BookBean.TYPE_WEB
                    )
                )
                val chapterBeanList = chapterList.map {
                    BookChapterBean(
                        name = it.nameRaw,
                        order = it.order,
                        url = it.url,
                        bookId = book.id,
                        isDownload = false
                    )
                }
                database.bookDao().setOrIgnoreChapter(chapterBeanList)
                bookService.setChapterContent(book.id) {
                    val progress = it.completed.get() * 100f / it.total
                    item.updateProgress(progress)
                    update(item)
                }
            }
        }
    }

    //region 网址扫描


    private var dataList = listOf<Tree>()
    private val dataSource = MutableStateFlow(dataList)
    val dataSourceFlow = dataSource.asStateFlow()

    fun submitList(data: List<Tree>) {
        dataList = data
        dataSource.value = dataList
    }

    private var dataListFilter = listOf<WebSiteConfigBean>()
    fun filterList(data: List<WebSiteConfigBean>) {
        dataListFilter = data
        val newDataList = mutableListOf<Tree>()
        val regexList = data.associate { it.key to it.value.toRegex() }
        for (item in dataList) {
            var isMatch = true
            for ((key, regex) in regexList) {
                isMatch = when (key) {
                    LINK_NAME -> item.name.matches(regex)
                    LINK_PATH -> item.absolutePath.matches(regex)
                    else -> true
                }
                if (!isMatch) {
                    break
                }
            }
            if (isMatch) {
                newDataList.add(item)
            }
        }
        dataSource.value = newDataList
    }

    //endregion
}